import { AxiosRequestConfig, AxiosResponse, AxiosPromise } from './constant'
import { createError } from './utils/error'
import { parseHeaders } from './utils/header'

export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => {
    const { data, method = 'get', url, headers, responseType, timeout } = config
    const XML = new XMLHttpRequest()
    // 如果设置返回类型，则更新
    if (responseType) {
      XML.responseType = responseType
    }
    XML.open(method.toUpperCase(), url!, true)

    // 监听请求状态
    XML.onreadystatechange = function handleLoad() {
      if (XML.readyState !== 4) {
        return
      }
      // 网络错误和超时错误时 status都为0
      if (XML.status === 0) {
        return
      }

      // 组装resolve的数据
      const responseHeaders = parseHeaders(XML.getAllResponseHeaders())
      // 返回数据 判断是否是否是文本
      const responseData = responseType && responseType !== 'text' ? XML.response : XML.responseText
      const response: AxiosResponse = {
        data: responseData,
        config,
        headers: responseHeaders,
        status: XML.status,
        request: XML,
        statusText: XML.statusText
      }
      handleResponse(response)
    }

    // 网络异常
    XML.onerror = function onError() {
      reject(createError('Network Error', config, null, XML))
    }
    // 超时错误
    if (timeout) {
      XML.timeout = timeout
    }
    XML.ontimeout = function onTimeOut() {
      reject(createError(`Timeout of ${timeout} ms exceeded`, config, '10000', XML))
    }
    // 处理非200状态码
    function handleResponse(response: AxiosResponse) {
      if (response.status >= 200 && response.status < 300) {
        resolve(response)
      } else {
        reject(
          createError(
            `Request failed with status code ${response.status}`,
            config,
            null,
            XML,
            response
          )
        )
      }
    }

    // 处理headers,真正添加请求头
    Object.keys(headers).forEach(name => {
      // 当data为null，header就不需要存在了
      if (data === null && name.toLowerCase() === 'content-type') {
        delete headers[name]
      } else {
        XML.setRequestHeader(name, headers[name])
      }
    })

    XML.send(data)
  })
}
